package com.kfyang.manualgear.generator.declare;

import com.kfyang.manualgear.annotation.crud.Construct;
import com.kfyang.manualgear.annotation.crud.Query;
import com.kfyang.manualgear.annotation.crud.ResultMap;
import com.kfyang.manualgear.constant.Constants;
import com.kfyang.manualgear.struct.Handler;
import com.kfyang.manualgear.struct.ParameterInfo;
import com.kfyang.manualgear.struct.ReturnTypeInfo;
import com.kfyang.manualgear.util.CheckUtils;
import com.kfyang.manualgear.util.ClassUtils;
import com.kfyang.manualgear.util.GlobalConfigUtils;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;

import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import java.util.*;

import static com.kfyang.manualgear.constant.Constants.LEFT_SHARP_BRACE;
import static com.kfyang.manualgear.constant.Constants.ORIGIN_TYPE;
import static com.kfyang.manualgear.constant.StatementConstants.*;
import static com.kfyang.manualgear.util.CheckUtils.checkNotNull;

/**
 * 生成标注了 Query 注解的方法实现
 */
public abstract class QueryMethodGenerator extends AbstractGenerator {
    /**
     * 字段名到 Handler 的哈希表
     */
    protected final Map<String, Handler> fieldToHandler = new HashMap<>();
    /**
     * 构造器参数列表
     */
    protected final List<ParameterInfo> constructorList = new ArrayList<>(); // 变量名、类型、get方法

    /**
     * 返回类型
     */
    protected ReturnTypeInfo returnTypeInfo;
    protected TypeName returnTypeClass;

    /**
     * 是否使用简单方法
     */
    boolean useSimple;

    public QueryMethodGenerator(ExecutableElement element, ClassWrapper classWrapper, boolean useSimple) {
        super(element, classWrapper);

        if (methodInfo.methodName == null) {
            Query queryAnno = element.getAnnotation(Query.class);
            if (queryAnno != null) {
                methodInfo.type = Constants.QUERY;
                methodInfo.methodName = element.getSimpleName().toString();
                methodInfo.cols = queryAnno.col();
                methodInfo.sql = queryAnno.value();
            }
        }
        this.useSimple = useSimple;
    }

    @Override
    protected void init() {
        super.init();

        if (methodInfo.resultMap == null && methodInfo.construct == null) {
            ResultMap resultMap = element.getAnnotation(ResultMap.class);
            if (resultMap != null) {
                methodInfo.resultMap = resultMap.value();
            } else {
                Construct construct = element.getAnnotation(Construct.class);

                checkNotNull(construct, "必须要注明返回值");

                methodInfo.construct = construct.value();
            }
        }

        // 获取返回实体类型
        DeclaredType declaredType = (DeclaredType) element.getReturnType();
        TypeMirror returnTypeMirror = null;

        try {
            if (!declaredType.getTypeArguments().isEmpty()) { // 泛型
                String returnClass = declaredType.toString();
                returnClass = returnClass.substring(0, returnClass.indexOf(LEFT_SHARP_BRACE));
                returnTypeInfo = GlobalConfigUtils.returnTypeMap.get(returnClass);
                returnTypeMirror = declaredType.getTypeArguments().get(returnTypeInfo.typePos);
            } else { // 非泛型
                returnTypeMirror = declaredType;
                String returnClass = declaredType.toString();
                returnTypeInfo = GlobalConfigUtils.returnTypeMap.get(returnClass);
            }
        } catch (Exception ignore) {
        }
        if (returnTypeInfo == null) { // 其他类型处理器不能处理
            returnTypeMirror = declaredType;
            returnTypeInfo = GlobalConfigUtils.returnTypeMap.get(ORIGIN_TYPE);
        }
        returnTypeClass = ClassName.get(returnTypeMirror);

        initFieldToHandlerMap(returnTypeMirror);
    }

    /**
     * 初始化 fieldToHandler 哈希表
     * @param returnTypeMirror 返回类型
     */
    private void initFieldToHandlerMap(TypeMirror returnTypeMirror) {
        TypeElement returnTypeElement = ClassUtils.mirrorToType(returnTypeMirror);
        if (methodInfo.resultMap != null) { // @ResultMap
            // 迭代获取实体类型的所有属性并缓存进fieldToGets中
            while (returnTypeElement != GlobalConfigUtils.objectElement) {
                Map<String, Handler> tempFieldToGet = GlobalConfigUtils.fieldToHandlerMaps.get(returnTypeElement);
                if (tempFieldToGet == null) {
                    for (Element each : returnTypeElement.getEnclosedElements()) {
                        if (each.getKind() == ElementKind.FIELD) {
                            try {
                                fieldToHandler.put(each.getSimpleName().toString(), GlobalConfigUtils.getTypeHandler(each.asType().toString()));
                            } catch (RuntimeException ignore) {
                            }
                        }
                    }
                    GlobalConfigUtils.fieldToHandlerMaps.put(returnTypeElement, new HashMap<>(fieldToHandler));
                } else {
                    fieldToHandler.putAll(tempFieldToGet);
                }

                returnTypeElement = ClassUtils.mirrorToType(returnTypeElement.getSuperclass());
            }
        } else { // @Construct
            List<? extends Element> constructs = returnTypeElement.getEnclosedElements().stream()
                .filter(innerElement -> {
                    if (innerElement.getKind() != ElementKind.CONSTRUCTOR) {
                        return false;
                    }
                    Construct constructAnno = innerElement.getAnnotation(Construct.class);
                    return constructAnno != null && constructAnno.value().equals(methodInfo.construct);
                })
                .toList();
            CheckUtils.checkSize(constructs, 1, "构造器错误");

            ExecutableElement constructor = (ExecutableElement) constructs.get(0);

            // 生成构造器参数列表
            Map<String, List<ParameterInfo>> constructorMap = GlobalConfigUtils.constructorMaps.get(returnTypeElement);
            List<ParameterInfo> tempConstructorList = null;
            if (constructorMap != null) {
                tempConstructorList = constructorMap.get(methodInfo.construct);
            }
            if (tempConstructorList == null) {
                for (VariableElement parameter : constructor.getParameters()) {
                    Handler handler = GlobalConfigUtils.getTypeHandler(parameter.asType().toString());
                    checkNotNull(handler, "没有对应的类型处理器");
                    constructorList.add(new ParameterInfo(parameter.getSimpleName().toString(),
                        ClassUtils.strToClass(parameter.asType().toString()), handler));
                }
            } else {
                constructorList.addAll(tempConstructorList);
            }
        }
    }

    /**
     * 拼接 sql
     */
    protected void addSql() {
        StringBuilder selectCode = new StringBuilder(QUERY_SQL_DEFINE + Constants.SELECT);
        // 结果列表
        StringJoiner colAndAlias = new StringJoiner(Constants.COMMA);
        String[] colExp = methodInfo.cols;
        for (int i = 0; i < colExp.length; i++) {
            colAndAlias.add(colExp[i] + Constants.BLANK + GlobalConfigUtils.alias[i]);
        }
        selectCode.append(colAndAlias).append(" \")");
        methodBuilder.addStatement(selectCode.toString());
        generateSqlAndFrag(methodInfo.sql);
    }

    /**
     * @return 是否支持
     */
    protected boolean notSupport() {
        return (useSimple && GlobalConfigUtils.methodSet.contains(methodName())) ||
            !Constants.QUERY.equals(methodInfo.type);
    }
}
